home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / netprog.zip / NETPROG.TAR / rlogin / rlogin.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  27KB  |  1,081 lines

  1. /*
  2.  * Copyright (c) 1983 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)rlogin.c    5.12 (Berkeley) 9/19/88";
  26. #endif /* not lint */
  27.  
  28. /*
  29.  * rlogin - remote login client.
  30.  */
  31.  
  32. #include <sys/param.h>
  33. #include <sys/file.h>
  34. #include <sys/socket.h>
  35. #include <sys/time.h>
  36. #include <sys/resource.h>
  37. #include <sys/wait.h>
  38.  
  39. #include <netinet/in.h>
  40.  
  41. #include <stdio.h>
  42. #include <sgtty.h>
  43. #include <pwd.h>
  44. #include <signal.h>
  45. #include <setjmp.h>
  46. #include <netdb.h>
  47. #include <errno.h>
  48. extern int    errno;
  49.  
  50. /*
  51.  * The server sends us a TIOCPKT_WINDOW notification when it starts up.
  52.  * The value for this (0x80) can't overlap the kernel defined TIOCKPT_xxx
  53.  * values.
  54.  */
  55.  
  56. #ifndef    TIOCPKT_WINDOW
  57. #define    TIOCPKT_WINDOW 0x80
  58. #endif
  59.  
  60. #ifndef    SIGUSR1
  61. #define    SIGUSR1 30    /* concession to sun */
  62. #endif
  63.  
  64. char        *index(), *rindex(), *malloc(), *getenv(), *strcat(), *strcpy();
  65. struct passwd    *getpwuid();
  66. char        *name;
  67. int        sockfd;            /* socket to server */
  68.  
  69. char        escchar = '~';        /* can be changed with -e flag */
  70. int        eight;            /* can be changed with -8 flag */
  71. int        litout;            /* can be changed with -L flag */
  72.  
  73. char        *speeds[] = {
  74.     "0", "50", "75", "110", "134", "150", "200", "300",
  75.     "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400"
  76. };
  77. char        term[256] = "network";
  78. int        dosigwinch = 0;        /* set to 1 if the server supports
  79.                        our window-size-change protocol */
  80.  
  81. #ifndef    sigmask
  82. #define    sigmask(m)    (1 << ((m)-1))
  83. #endif
  84.  
  85. #ifdef sun
  86. struct winsize {
  87.     unsigned short    ws_row;
  88.     unsigned short    ws_col;
  89.     unsigned short    ws_xpixel;
  90.     unsigned short    ws_ypixel;
  91. };
  92. #endif
  93.  
  94. struct winsize    currwinsize;        /* current size of window */
  95.  
  96. int    sigpipe_parent();        /* our signal handlers */
  97. int    sigwinch_parent();
  98. int    sigcld_parent();
  99. int    sigurg_parent();
  100. int    sigusr1_parent();
  101. int    sigurg_child();
  102.  
  103. /*
  104.  * The following routine provides compatibility (such as it is)
  105.  * between 4.2BSD Suns and others.  Suns have only a `ttysize',
  106.  * so we convert it to a winsize.
  107.  */
  108.  
  109. #ifdef sun
  110.  
  111. int
  112. get_window_size(fd, wp)
  113. int        fd;
  114. struct winsize    *wp;
  115. {
  116.     struct ttysize    ts;
  117.     int        error;
  118.  
  119.     if ( (error = ioctl(0, TIOCGSIZE, &ts)) != 0)
  120.         return(error);
  121.     wp->ws_row    = ts.ts_lines;
  122.     wp->ws_col    = ts.ts_cols;
  123.     wp->ws_xpixel = 0;
  124.     wp->ws_ypixel = 0;
  125.     return(0);
  126. }
  127. #else
  128. #define    get_window_size(fd, wp)        ioctl(fd, TIOCGWINSZ, wp)
  129. #endif    /* sun */
  130.  
  131. main(argc, argv)
  132. int    argc;
  133. char    **argv;
  134. {
  135.     char        *host, *cp;
  136.     struct sgttyb    ttyb;
  137.     struct passwd    *pwd;
  138.     struct servent    *sp;
  139.     int        uid, options = 0, oldsigmask;
  140.     int        on = 1;
  141.  
  142.     if ( (host = rindex(argv[0], '/')) != NULL)
  143.         host++;
  144.     else
  145.         host = argv[0];
  146.     argv++, --argc;
  147.     if (strcmp(host, "rlogin") == 0)
  148.         host = *argv++, --argc;
  149. another:
  150.     if (argc > 0 && strcmp(*argv, "-d") == 0) {
  151.         /*
  152.          * Turn on the debug option for the socket.
  153.          */
  154.  
  155.         argv++, argc--;
  156.         options |= SO_DEBUG;
  157.         goto another;
  158.     }
  159.  
  160.     if (argc > 0 && strcmp(*argv, "-l") == 0) {
  161.         /*
  162.          * Specify the server-user-name, instead of using the
  163.          * name of the person invoking us.
  164.          */
  165.  
  166.         argv++, argc--;
  167.         if (argc == 0)
  168.             goto usage;
  169.         name = *argv++; argc--;
  170.         goto another;
  171.     }
  172.  
  173.     if (argc > 0 && strncmp(*argv, "-e", 2) == 0) {
  174.         /*
  175.          * Specify an escape character, instead of the default tilde.
  176.          */
  177.  
  178.         escchar = argv[0][2];
  179.         argv++, argc--;
  180.         goto another;
  181.     }
  182.  
  183.     if (argc > 0 && strcmp(*argv, "-8") == 0) {
  184.         /*
  185.          * 8-bit input.  Specifying this forces us to use RAW mode
  186.          * input from the user's terminal.  Also, in this mode we
  187.          * won't perform any local flow control.
  188.          */
  189.  
  190.         eight = 1;
  191.         argv++, argc--;
  192.         goto another;
  193.     }
  194.  
  195.     if (argc > 0 && strcmp(*argv, "-L") == 0) {
  196.         /*
  197.          * 8-bit output.  Causes us to set the LLITOUT flag,
  198.          * which tells the line discpline: no output translations.
  199.          */
  200.  
  201.         litout = 1;
  202.         argv++, argc--;
  203.         goto another;
  204.     }
  205.  
  206.     if (host == NULL)
  207.         goto usage;
  208.     if (argc > 0)
  209.         goto usage;    /* too many command line arguments */
  210.  
  211.     /*
  212.      * Get the name of the user invoking us: the client-user-name.
  213.      */
  214.  
  215.     if ( (pwd = getpwuid(getuid())) == NULL) {
  216.         fputs("Who are you?\n", stderr);
  217.         exit(1);
  218.     }
  219.  
  220.     /*
  221.      * Get the name of the server we connect to.
  222.      */
  223.  
  224.     if ( (sp = getservbyname("login", "tcp")) == NULL) {
  225.         fputs("rlogin: login/tcp: unknown service\n", stderr);
  226.         exit(2);
  227.     }
  228.  
  229.     /*
  230.      * Get the name of the terminal from the environment.
  231.      * Also get the terminal's speed.  Both the name and
  232.      * the speed are passed to the server as the "cmd"
  233.      * argument of the rcmd() function.  This is something
  234.      * like "vt100/9600".
  235.      */
  236.  
  237.     if ( (cp = getenv("TERM")) != NULL)
  238.         strcpy(term, cp);
  239.     if (ioctl(0, TIOCGETP, &ttyb) == 0) {
  240.         strcat(term, "/");
  241.         strcat(term, speeds[ttyb.sg_ospeed]);
  242.     }
  243.  
  244.     get_window_size(0, &currwinsize);
  245.     signal(SIGPIPE, sigpipe_parent);
  246.  
  247.     /*
  248.      * Block the SIGURG and SIGUSR1 signals.  These will be handled
  249.      * by the parent and the child after the fork.
  250.      */
  251.  
  252.     oldsigmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
  253.  
  254.     /*
  255.      * Use rcmd() to connect to the server.  Note that even though
  256.      * we're using rcmd, we specify the port number of the rlogin
  257.      * server, not the rshd server.  We also pass the terminal-type/speed
  258.      * as the "command" argument, but the server knows what it is.
  259.      */
  260.  
  261.         sockfd = rcmd(&host, sp->s_port, pwd->pw_name,
  262.                 name ? name : pwd->pw_name, term, (int *) 0);
  263.         if (sockfd < 0)
  264.                 exit(1);
  265.  
  266.     if ((options & SO_DEBUG) &&
  267.         setsockopt(sockfd, SOL_SOCKET, SO_DEBUG, &on, sizeof(on)) < 0)
  268.         perror("rlogin: setsockopt (SO_DEBUG)");
  269.  
  270.     /*
  271.      * Now change to the real user ID.  We have to be set-user-ID root
  272.      * to get the privileged port that rcmd() uses,
  273.      * however we now want to run as the real user who invoked us.
  274.      */
  275.  
  276.     uid = getuid();
  277.     if (setuid(uid) < 0) {
  278.         perror("rlogin: setuid");
  279.         exit(1);
  280.     }
  281.  
  282.     doit(oldsigmask);
  283.     /*NOTREACHED*/
  284. usage:
  285.     fputs("usage: rlogin host [ -ex ] [ -l username ] [ -8 ] [ -L ]\n",
  286.         stderr);
  287.     exit(1);
  288. }
  289.  
  290. int    childpid;
  291.  
  292. /*
  293.  * tty flags.  Refer to tty(4) for all the details.
  294.  */
  295.  
  296. int        defflags;    /* the sg_flags word from the sgttyb struct */
  297. int        tabflag;    /* the two tab bits from the sg_flags word */
  298. int        deflflags;
  299. char        deferase;    /* client's erase character */
  300. char        defkill;    /* client's kill character */
  301. struct tchars    deftc;
  302. struct ltchars    defltc;
  303.  
  304. /*
  305.  * If you set one of the special terminal characters to -1, that effectively
  306.  * disables the line discipline from processing that special character.
  307.  * We initialize the following two structures to do this.  However, the
  308.  * code below replaces the -1 entries for "stop-output" and "start-output"
  309.  * with the actual values of these two characters (such as ^Q/^S).
  310.  * This way, we can use CBREAK mode but only have the line discipline do
  311.  * flow control.  All other special characters are ignored by our end and
  312.  * passed to the server's line discipline.
  313.  */
  314.  
  315. struct tchars    notc =    { -1, -1, -1, -1, -1, -1 };
  316.                 /* disables all the tchars: interrupt, quit,
  317.                    stop-output, start-output, EOF */
  318. struct ltchars    noltc =    { -1, -1, -1, -1, -1, -1 };
  319.                 /* disables all ltchars: suspend,
  320.                    delayed-suspend, reprint-line, flush,
  321.                    word-erase, literal-next */
  322.  
  323. doit(oldsigmask)
  324. int    oldsigmask;        /* mask of blocked signals */
  325. {
  326.     int        exit();
  327.     struct sgttyb    sb;
  328.  
  329.     ioctl(0, TIOCGETP, (char *) &sb);    /* get the basic modes */
  330.     defflags  = sb.sg_flags;
  331.     tabflag   = defflags & TBDELAY;        /* save the 2 tab bits */
  332.     defflags &= ECHO | CRMOD;
  333.     deferase  = sb.sg_erase;
  334.     defkill   = sb.sg_kill;
  335.  
  336.     ioctl(0, TIOCLGET, (char *) &deflflags);
  337.  
  338.     ioctl(0, TIOCGETC, (char *) &deftc);
  339.     notc.t_startc = deftc.t_startc;        /* replace -1 with start char */
  340.     notc.t_stopc  = deftc.t_stopc;        /* replace -1 with stop char */
  341.  
  342.     ioctl(0, TIOCGLTC, (char *) &defltc);
  343.  
  344.     signal(SIGINT, SIG_IGN);
  345.     setsignal(SIGHUP, exit);    /* HUP or QUIT go straight to exit() */
  346.     setsignal(SIGQUIT, exit);
  347.  
  348.     if ( (childpid = fork()) < 0) {
  349.         perror("rlogin: fork");
  350.         done(1);
  351.     }
  352.     if (childpid == 0) {            /* child process == reader */
  353.         tty_mode(1);
  354.         if (reader(oldsigmask) == 0) {
  355.             /*
  356.              * If the reader() returns 0, the socket to the
  357.              * server returned an EOF, meaning the client
  358.              * logged out of the remote system.
  359.              * This is the normal termination.
  360.              */
  361.  
  362.             prf("Connection closed.");
  363.             exit(0);
  364.         }
  365.  
  366.         /*
  367.          * If the reader() returns nonzero, the socket to the
  368.          * server returned an error.  Something went wrong.
  369.          */
  370.  
  371.         sleep(1);
  372.         prf("\007Connection closed.");    /* 007 = ASCII bell */
  373.         exit(3);
  374.     }
  375.  
  376.     /*
  377.      * Parent process == writer.
  378.      *
  379.      * We may still own the socket, and may have a pending SIGURG
  380.      * (or might receive one soon) that we really want to send to
  381.      * the reader.  Set a trap that copies such signals to
  382.      * the child.  Once the two signal handlers are installed,
  383.      * reset the signal mask to what it was before the fork.
  384.      */
  385.  
  386.     signal(SIGURG, sigurg_parent);
  387.     signal(SIGUSR1, sigusr1_parent);
  388.     sigsetmask(oldsigmask);        /* reenables SIGURG and SIGUSR1 */
  389.  
  390.     signal(SIGCHLD, sigcld_parent);
  391.  
  392.     writer();
  393.  
  394.     /*
  395.      * If the writer returns, it means the user entered "~." on the
  396.      * terminal.  In this case we terminate and the server will
  397.      * eventually get an EOF on its end of the network connection.
  398.      * This should cause the server to log you out on the remote system.
  399.      */
  400.  
  401.     prf("Closed connection.");
  402.     done(0);
  403. }
  404.  
  405. /*
  406.  * Enable a signal handler, unless the signal is already being ignored.
  407.  * This function is called before the fork(), for SIGHUP and SIGQUIT.
  408.  */
  409.  
  410. setsignal(sig, action)
  411. int    sig;
  412. int    (*action)();
  413. {
  414.     register int    omask;
  415.  
  416.     omask = sigblock(sigmask(sig));        /* block the signal */
  417.  
  418.     if (signal(sig, action) == SIG_IGN)
  419.         signal(sig, SIG_IGN);
  420.  
  421.     sigsetmask(omask);            /* reset the signal mask */
  422. }
  423.  
  424. /*
  425.  * This function is called by the parent:
  426.  *    (1) at the end (user terminates the client end);
  427.  *    (2) SIGCLD signal - the sigcld_parent() function;
  428.  *    (3) SIGPIPE signal - the connection has dropped.
  429.  *
  430.  * We send the child a SIGKILL signal, which it can't ignore, then
  431.  * wait for it to terminate.
  432.  */
  433.  
  434. done(status)
  435. int    status;        /* exit() status */
  436. {
  437.     int    w;
  438.  
  439.     tty_mode(0);    /* restore the user's terminal mode */
  440.  
  441.     if (childpid > 0) {
  442.         signal(SIGCHLD, SIG_DFL);    /* disable signal catcher */
  443.  
  444.         if (kill(childpid, SIGKILL) >= 0)
  445.             while ((w = wait((union wait *) 0)) > 0 &&
  446.                    w != childpid)
  447.                 ;
  448.     }
  449.     exit(status);
  450. }
  451.  
  452. /*
  453.  * Copy SIGURGs to the child process.
  454.  * The parent shouldn't get any SIGURGs, but if it does, just pass
  455.  * them to the child, as it's the child that handles the out-of-band
  456.  * data from the server.
  457.  */
  458.  
  459. sigurg_parent()
  460. {
  461.     kill(childpid, SIGURG);
  462. }
  463.  
  464. /*
  465.  * The child sends the parent a SIGUSR1 signal when the child receives
  466.  * the TIOCPKT_WINDOW indicator from the server.  This tells the
  467.  * client to enable the in-band window-changing protocol.
  468.  */
  469.  
  470. sigusr1_parent()
  471. {
  472.     if (dosigwinch == 0) {        /* first time */
  473.         /*
  474.          * First time.  Send the initial window sizes to the
  475.          * server and enable the SIGWINCH signal, so that we pick
  476.          * up any changes from this point on.
  477.          */
  478.  
  479.         sendwindow();
  480.         signal(SIGWINCH, sigwinch_parent);
  481.         dosigwinch = 1;
  482.     }
  483. }
  484.  
  485. /*
  486.  * SIGCLD signal haldner in parent.
  487.  */
  488.  
  489. sigcld_parent()
  490. {
  491.     union wait    status;
  492.     register int    pid;
  493.  
  494. again:
  495.     /*
  496.      * WNOHANG -> don't block.
  497.      * WUNTRACED -> tell us about stopped, untraced children.
  498.      */
  499.  
  500.     pid = wait3(&status, WNOHANG | WUNTRACED, (struct rusage *) 0);
  501.     if (pid == 0)
  502.         return;        /* no processes wish to report status */
  503.  
  504.     /*
  505.      * If the child (reader) dies, just quit.
  506.      */
  507.  
  508.     if (pid < 0 || (pid == childpid && WIFSTOPPED(status) == 0))
  509.         done( (int) (status.w_termsig | status.w_retcode) );
  510.     goto again;
  511. }
  512.  
  513. /*
  514.  * SIGPIPE signal handler.  We're called if the connection drops.
  515.  * This signal happens in the parent, since the signal is sent to the process
  516.  * that writes to the socket (pipe) that has no reader.
  517.  */
  518.  
  519. sigpipe_parent()
  520. {
  521.     signal(SIGPIPE, SIG_IGN);
  522.     prf("\007Connection closed.");
  523.     done(1);
  524. }
  525.  
  526. /*******************************************************************************
  527.  *
  528.  * writer main loop: copy standard input (user's terminal) to network.
  529.  *
  530.  * The standard input is in raw mode, however, we look for three special
  531.  * sequences of characters:
  532.  *
  533.  *    ~.    terminate;
  534.  *    ~^D    terminate;
  535.  *    ~^Z    suspend rlogin process;
  536.  *    ~^Y    suspend rlogin process, but leave reader alone.
  537.  *
  538.  * This handling of escape sequences isn't perfect, however.  For example,
  539.  * use rlogin, then run the vi editor on the remote system.  Enter return,
  540.  * then tilde (vi's convert-case-of-character command), then dot (vi's redo
  541.  * last command).  Voila, you're logged out.
  542.  */
  543.  
  544. writer()
  545. {
  546.     char        c;
  547.     register    n;
  548.     register    bol = 1;               /* beginning of line */
  549.     register    local = 0;
  550.  
  551.     for ( ; ; ) {
  552.         /*
  553.          * Since we have to look at every character entered by the
  554.          * user, we read the standard input one-character-at-a-time.
  555.          * For human input, this isn't too bad.
  556.          */
  557.  
  558.         n = read(0, &c, 1);
  559.         if (n <= 0) {
  560.             if (n < 0 && errno == EINTR)
  561.                 continue;
  562.             break;
  563.         }
  564.  
  565.         /*
  566.          * If we're at the beginning of the line and recognize
  567.          * the escape character, then we echo the next character
  568.          * locally.  If the command character is doubled, for example
  569.          * if you enter ~~. at the beginning of a line, nothing
  570.          * is echoed locally and ~. is sent to the server.
  571.          */
  572.  
  573.         if (bol) {
  574.             bol = 0;
  575.             if (c == escchar) {
  576.                 local = 1;    /* local echo next char */
  577.                 continue;    /* next iteration of for-loop */
  578.             }
  579.  
  580.         } else if (local) {
  581.             /*
  582.              * The previous character (the first character of
  583.              * a line) was the escape character.  Look at the
  584.              * second character of the line and determine if
  585.              * something special should happen.
  586.              */
  587.  
  588.             local = 0;
  589.  
  590.             if (c == '.' || c == deftc.t_eofc) {
  591.                 /*
  592.                  * A tilde-period or tilde-EOF terminates
  593.                  * the parent.  Echo the period or EOF
  594.                  * then stop.
  595.                  */
  596.  
  597.                 echo(c);
  598.                 break;        /* breaks out of for-loop */
  599.             }
  600.  
  601.             if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
  602.                 /*
  603.                  * A tilde-^Z or tilde-^Y stops the parent
  604.                  * process.
  605.                  */
  606.  
  607.                 bol = 1;
  608.                 echo(c);
  609.  
  610.                 stop(c); /* returns only when we're continued */
  611.  
  612.                 continue;    /* next iteration of for-loop */
  613.             }
  614.  
  615.             /*
  616.              * If the input was tilde-someothercharacter,
  617.              * then we have to write both the tilde and the
  618.              * other character to the network.
  619.              */
  620.  
  621.             if (c != escchar)
  622.                 if (write(sockfd, &escchar, 1) != 1) {
  623.                     prf("line gone");
  624.                     break;
  625.                 }
  626.         }
  627.  
  628.         if (write(sockfd, &c, 1) != 1) {
  629.             prf("line gone");
  630.             break;
  631.         }
  632.  
  633.         /*
  634.          * Set a flag if by looking at the current character
  635.          * we think the next character is going to be the first
  636.          * character of a line.  This ain't perfect.
  637.          */
  638.  
  639.         bol = (c == defkill) ||        /* kill char, such as ^U */
  640.               (c == deftc.t_eofc) ||    /* EOF char, such as ^D */
  641.               (c == deftc.t_intrc) ||    /* interrupt, such as ^C */
  642.               (c == defltc.t_suspc) ||    /* suspend job, such as ^Z */
  643.               (c == '\r') ||        /* carriage-return */
  644.               (c == '\n');        /* newline */
  645.     }
  646. }
  647.  
  648. /*
  649.  * Echo a character on the standard output (the user's terminal).
  650.  * This is called only by the writer() function above to handle the
  651.  * escape characters that we echo.
  652.  */
  653.  
  654. echo(c)
  655. register char    c;
  656. {
  657.     char        buf[8];
  658.     register char    *p = buf;
  659.  
  660.     *p++ = escchar;        /* print the escape character first */
  661.  
  662.     c &= 0177;
  663.     if (c < 040) {
  664.         /*
  665.          * Echo ASCII control characters as a caret, followed
  666.          * by the upper case character.
  667.          */
  668.  
  669.         *p++ = '^';
  670.         *p++ = c + '@';
  671.  
  672.     } else if (c == 0177) {        /* ASCII DEL character */
  673.         *p++ = '^';
  674.         *p++ = '?';
  675.  
  676.     } else
  677.         *p++ = c;
  678.  
  679.     *p++ = '\r';    /* need a return-linefeed, since it's in raw mode */
  680.     *p++ = '\n';
  681.  
  682.     write(1, buf, p - buf);
  683. }
  684.  
  685. /*
  686.  * Stop the parent process (job control).
  687.  * If the character entered by the user is the "stop process" (^Z) character,
  688.  * then we send the SIGTSTP signal to both ourself and the reader (all the
  689.  * processes in the sending processes process group).  When this happens,
  690.  * anything sent by the server to us will be buffered by the network
  691.  * until the reader starts up again and reads it.
  692.  * However, if the character is the "delayed stop process" (^Y) character,
  693.  * then we stop only ourself and not the reader.  This way, the reader
  694.  * continues outputting any data that it receives from the server.
  695.  */
  696.  
  697. stop(cmdc)
  698. char    cmdc;
  699. {
  700.     tty_mode(0);        /* first reset the terminal mode to normal */
  701.  
  702.     signal(SIGCHLD, SIG_IGN);  /* ignore SIGCLD in case child stops too */
  703.  
  704.     kill( (cmdc == defltc.t_suspc) ? 0 : getpid() , SIGTSTP);
  705.  
  706.         /* resumes here when we're continued by user */
  707.     signal(SIGCHLD, sigcld_parent);
  708.     tty_mode(1);        /* reset terminal back to raw mode */
  709.  
  710.     sigwinch_parent();    /* see if the window size has changed */
  711. }
  712.  
  713. /*
  714.  * SIGWINCH signal handler.
  715.  * We're also called above, after we've been resumed after being stopped.
  716.  * We only send a window size message to the server if the size has changed.
  717.  * Note that we use the flag "dosigwinch" to indicate if the server supports
  718.  * our window-size-change protocol.  If the server doesn't tell us that
  719.  * it supports it (see sigusr1_parent() above), we'll never send it.
  720.  */
  721.  
  722. sigwinch_parent()
  723. {
  724.     struct winsize    ws;
  725.  
  726.     if (dosigwinch && (get_window_size(0, &ws) == 0) &&
  727.         (bcmp((char *) &ws, (char *) &currwinsize,
  728.                         sizeof(struct winsize)) != 0)) {
  729.         currwinsize = ws;    /* store new size for next time */
  730.         sendwindow();        /* and tell the server */
  731.     }
  732. }
  733.  
  734. /*
  735.  * Send the window size to the server via the magic escape.
  736.  * Note that we send the 4 unsigned shorts in the structure in network byte
  737.  * order, as it's possible to be running the client and server on systems
  738.  * with different byte orders (a VAX and a Sun, for example).
  739.  */
  740.  
  741. sendwindow()
  742. {
  743.     char            obuf[4 + sizeof(struct winsize)];
  744.     register struct winsize    *wp;
  745.  
  746.     wp = (struct winsize *)(obuf + 4);
  747.  
  748.     obuf[0] = 0377;        /* these 4 bytes are the magic sequence */
  749.     obuf[1] = 0377;
  750.     obuf[2] = 's';
  751.     obuf[3] = 's';
  752.  
  753.     wp->ws_row    = htons(currwinsize.ws_row);
  754.     wp->ws_col    = htons(currwinsize.ws_col);
  755.     wp->ws_xpixel = htons(currwinsize.ws_xpixel);
  756.     wp->ws_ypixel = htons(currwinsize.ws_ypixel);
  757.  
  758.     write(sockfd, obuf, sizeof(obuf));
  759. }
  760.  
  761. /*******************************************************************************
  762.  *
  763.  * reader main loop: copy network to standard output (user's terminal).
  764.  */
  765.  
  766. char    rcvbuf[8 * 1024];    /* read into here from network */
  767. int    rcvcnt;            /* amount of data in rvcbuf[] */
  768. int    rcvstate;        /* READING or WRITING: so sigurg_child()
  769.                    knows whether a read or write system
  770.                    call was interrupted */
  771. int    parentpid;        /* parent pid, from the fork */
  772. jmp_buf    rcvtop;            /* setjmp/longjmp buffer */
  773.  
  774. #define    READING    1        /* values for rcvstate */
  775. #define    WRITING    2
  776.  
  777. reader(oldsigmask)
  778. int    oldsigmask;    /* signal mask from parent */
  779. {
  780. #if !defined(BSD) || BSD < 43
  781.     int    pid = -getpid();
  782. #else
  783.     int    pid = getpid();
  784. #endif
  785.     int    n, remaining;
  786.     char    *bufp = rcvbuf;
  787.  
  788.     signal(SIGTTOU, SIG_IGN);
  789.     signal(SIGURG, sigurg_child);    /* out-of-band data from server */
  790.     fcntl(sockfd, F_SETOWN, pid);    /* to receive SIGURG signals */
  791.  
  792.     parentpid = getppid();        /* for SIGUSR1 signal at beginning */
  793.  
  794.     setjmp(rcvtop);            /* see the longjmps in sigurg_child() */
  795.  
  796.     sigsetmask(oldsigmask);        /* reset signal mask */
  797.                     /* reenables SIGURG and SIGUSR1 */
  798.  
  799.     for ( ; ; ) {
  800.         /*
  801.          * Reader main loop - read as much as we can from
  802.          * the network and write it to standard output.
  803.          */
  804.  
  805.         while ( (remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
  806.             /*
  807.              * While there's data in the buffer to write,
  808.              * write it to the standard output.
  809.              */
  810.  
  811.             rcvstate = WRITING;
  812.             if ( (n = write(1, bufp, remaining)) < 0) {
  813.                 if (errno != EINTR)
  814.                     return(-1);
  815.                 continue;
  816.             }
  817.             bufp += n;    /* incr pointer past what we wrote */
  818.         }
  819.  
  820.         /*
  821.          * There's nothing in our buffer to write, so read from
  822.          * the network.
  823.          */
  824.  
  825.         bufp = rcvbuf;        /* ptr to start of buffer */
  826.         rcvcnt = 0;        /* #bytes in buffer */
  827.         rcvstate = READING;
  828.  
  829.         rcvcnt = read(sockfd, rcvbuf, sizeof(rcvbuf));
  830.         if (rcvcnt == 0)
  831.             return(0);    /* user logged out from remote system */
  832.         if (rcvcnt < 0) {
  833.             if (errno == EINTR)
  834.                 continue;
  835.             perror("read");
  836.             return(-1);
  837.         }
  838.     }
  839. }
  840.  
  841. /*
  842.  * This is the SIGURG signal handler in the child.  Here we process
  843.  * the out-of-band signals that arrive from the server.
  844.  */
  845.  
  846. sigurg_child()
  847. {
  848.     int        flushflag, atoobmark, n, rcvd;
  849.     char        waste[BUFSIZ], ctlbyte;
  850.     struct sgttyb    sb;
  851.  
  852.     rcvd = 0;
  853.     while (recv(sockfd, &ctlbyte, 1, MSG_OOB) < 0) {
  854.         switch (errno) {
  855.  
  856.         case EWOULDBLOCK:
  857.             /*
  858.              * The Urgent data is not here yet.
  859.              * It may not be possible to send it yet if we are
  860.              * blocked for output and our input buffer is full.
  861.              *
  862.              * First try to read as much as the receive buffer
  863.              * has room for.  Note that neither of the reads
  864.              * below will go past the OOB mark.
  865.              */
  866.  
  867.             if (rcvcnt < sizeof(rcvbuf)) {
  868.                 n = read(sockfd, rcvbuf + rcvcnt,
  869.                         sizeof(rcvbuf) - rcvcnt);
  870.                 if (n <= 0)
  871.                     return;
  872.  
  873.                 rcvd += n;    /* remember how much we read */
  874.  
  875.             } else {
  876.                 /*
  877.                  * The receive buffer is currently full.
  878.                  * We have no choice but to read into
  879.                  * our wastebasket.
  880.                  */
  881.  
  882.                 n = read(sockfd, waste, sizeof(waste));
  883.                 if (n <= 0)
  884.                     return;
  885.             }
  886.             continue;    /* try to read to OOB byte again */
  887.  
  888.         default:
  889.             return;
  890.         }
  891.     }
  892.  
  893.     /*
  894.      * Note that in the TIOCPKT mode, any number of the control
  895.      * bits may be on in the control byte, so we have to test
  896.      * for all the ones we're interested in.
  897.      */
  898.  
  899.     if (ctlbyte & TIOCPKT_WINDOW) {
  900.         /*
  901.          * We get this control byte from the server after it has
  902.          * started.  It means that the server is started and
  903.          * it needs to know the current window size.  We send
  904.          * the SIGUSR1 signal to the parent, as it is the
  905.          * parent who must send the window size to the server.
  906.          */
  907.  
  908.         kill(parentpid, SIGUSR1);
  909.     }
  910.  
  911.     if (!eight && (ctlbyte & TIOCPKT_NOSTOP)) {
  912.         /*
  913.          * Either the server is not using ^S/^Q or the server is
  914.          * in raw mode.  We must set the user's terminal to
  915.          * raw mode.  This disables flow control on the client system.
  916.          */
  917.  
  918.         ioctl(0, TIOCGETP, (char *) &sb);
  919.         sb.sg_flags &= ~CBREAK;            /* CBREAK off */
  920.         sb.sg_flags |= RAW;            /* RAW on */
  921.         ioctl(0, TIOCSETN, (char *) &sb);    /* doesn't delay */
  922.  
  923.         notc.t_stopc  = -1;            /* no stop char */
  924.         notc.t_startc = -1;            /* no start char */
  925.         ioctl(0, TIOCSETC, (char *) ¬c);
  926.     }
  927.  
  928.     if (!eight && (ctlbyte & TIOCPKT_DOSTOP)) {
  929.         /*
  930.          * The server is using ^S/^Q and it's not in raw mode,
  931.          * so we can do flow control on the client system.
  932.          */
  933.  
  934.         ioctl(0, TIOCGETP, (char *) &sb);
  935.         sb.sg_flags &= ~RAW;            /* RAW off */
  936.         sb.sg_flags |= CBREAK;            /* CBREAK on */
  937.         ioctl(0, TIOCSETN, (char *) &sb);
  938.  
  939.         notc.t_stopc  = deftc.t_stopc;        /* enable stop */
  940.         notc.t_startc = deftc.t_startc;        /* enable start */
  941.         ioctl(0, TIOCSETC, (char *) ¬c);
  942.     }
  943.  
  944.     if (ctlbyte & TIOCPKT_FLUSHWRITE) {
  945.         /*
  946.          * The terminal output queue on the server was flushed.
  947.          * First we flush our terminal output queue (the output
  948.          * queue for the user's terminal).
  949.          */
  950.  
  951.         flushflag = FWRITE;    /* flush output only, not input */
  952.         ioctl(1, TIOCFLUSH, (char *) &flushflag);
  953.  
  954.         /*
  955.          * Now we continue reading from the socket, throwing
  956.          * away all the data until we reach the out-of-band mark.
  957.          */
  958.  
  959.         for ( ; ; ) {
  960.             if (ioctl(sockfd, SIOCATMARK, &atoobmark) < 0) {
  961.                 perror("ioctl SIOCATMARK error");
  962.                 break;
  963.             }
  964.             if (atoobmark)
  965.                 break;    /* we're at the oob mark */
  966.  
  967.             if ( (n = read(sockfd, waste, sizeof(waste))) <= 0)
  968.                 break;
  969.         }
  970.  
  971.         /*
  972.          * We don't want any pending data that we've already read
  973.          * into the receive buffer to be output, so clear the receive
  974.          * buffer (i.e., just set rcvcnt = 0).
  975.          * Also, if we were hanging on a write to standard output
  976.          * when interrupted, we don't want it to restart, so we
  977.          * longjmp back to the top of the loop.
  978.          * If we were reading, we want to restart it anyway.
  979.          */
  980.  
  981.         rcvcnt = 0;
  982.         longjmp(rcvtop, 1);    /* back to the setjmp */
  983.                     /* the arg of 1 isn't used */
  984.     }
  985.  
  986.     /*
  987.      * If we read data into the receive buffer above (so that we
  988.      * could read the OOB byte) and if we we're interrupted during
  989.      * a read, then longjmp to the top of the loop to write the
  990.      * data that was received.
  991.      * Don't abort a pending write, however, or we won't know how
  992.      * much was written.
  993.      */
  994.  
  995.     if (rcvd > 0 && rcvstate == READING)
  996.         longjmp(rcvtop, 1);
  997.  
  998.     return;        /* from the signal handler; probably causes an EINTR */
  999. }
  1000.  
  1001. /*
  1002.  * Set the terminal mode.  This function affects the user's terminal.
  1003.  * We're called by both the parent and child.
  1004.  */
  1005.  
  1006. tty_mode(mode)
  1007. int    mode;        /* 0 -> reset to normal; 1 -> set for rlogin */
  1008. {
  1009.     struct tchars    *tcptr;
  1010.     struct ltchars    *ltcptr;
  1011.     struct sgttyb    sb;        /* basic modes */
  1012.     int        lflags;        /* local mode word */
  1013.  
  1014.     ioctl(0, TIOCGETP, (char *) &sb);
  1015.     ioctl(0, TIOCLGET, (char *) &lflags);
  1016.  
  1017.     switch (mode) {
  1018.  
  1019.     case 0:
  1020.         /*
  1021.          * This is called by the parent when it's done to reset
  1022.          * the terminal state to how it found it.
  1023.          * The parent also calls this to reset the terminal state
  1024.          * before stopping itself with job control.
  1025.          */
  1026.  
  1027.         sb.sg_flags &= ~(CBREAK | RAW | TBDELAY);
  1028.         sb.sg_flags |= defflags | tabflag;
  1029.  
  1030.         tcptr = &deftc;        /* restore all special chars */
  1031.         ltcptr = &defltc;
  1032.         sb.sg_kill = defkill;
  1033.         sb.sg_erase = deferase;
  1034.         lflags = deflflags;
  1035.         break;
  1036.  
  1037.     case 1:
  1038.         /*
  1039.          * This is called by the child when it starts, to set the
  1040.          * terminal to a raw mode.  Actually, we default to CBREAK
  1041.          * unless the -8 flag was specified (8-bit input) in which
  1042.          * case we have to use RAW mode.
  1043.          * The parent also calls this when resumed, after being
  1044.          * stopped by job control.
  1045.          */
  1046.  
  1047.         sb.sg_flags |= (eight ? RAW : CBREAK);
  1048.         sb.sg_flags &= ~defflags;
  1049.             /* preserve tab delays, but turn off XTABS */
  1050.         if ((sb.sg_flags & TBDELAY) == XTABS)
  1051.             sb.sg_flags &= ~TBDELAY;
  1052.  
  1053.         tcptr = ¬c;        /* disable all special chars */
  1054.         ltcptr = &noltc;
  1055.         sb.sg_kill = -1;
  1056.         sb.sg_erase = -1;
  1057.         if (litout)
  1058.             lflags |= LLITOUT;    /* no output translations */
  1059.         break;
  1060.  
  1061.     default:
  1062.         return;
  1063.     }
  1064.  
  1065.     ioctl(0, TIOCSLTC, (char *) ltcptr);
  1066.     ioctl(0, TIOCSETC, (char *) tcptr);
  1067.     ioctl(0, TIOCSETN, (char *) &sb);
  1068.     ioctl(0, TIOCLSET, (char *) &lflags);
  1069. }
  1070.  
  1071. /*
  1072.  * Fatal error.
  1073.  */
  1074.  
  1075. prf(str)
  1076. char    *str;
  1077. {
  1078.     fputs(str, stderr);
  1079.     fputs("\r\n", stderr);    /* return & newline, in case raw mode */
  1080. }
  1081.